/*
 * FtpContext.cpp
 *
 *  Created on: 2012-12-28
 *      Author: yangentao@gmail.com
 */
#include <iostream>
#include <fstream>
#include <boost/regex.hpp>
#include <boost/bind.hpp>
#include "FtpContext.h"
#include "../yet/LineParse.h"
#include "../yet/baseutil.h"
#include "../yet/File.h"

namespace ftp {
using namespace yet;

bool myFtpLog(LogMsg msg) {
	if (msg.tag == "ftpsend") {
		if (msg.level == logError) {
			cout << "[S] " << msg.msg << endl;
		} else {
			cout << "[S] " << msg.msg << endl;
		}
	} else if (msg.tag == "ftpecho") {
		if (msg.level == logError) {
			cout << "[R] " << msg.msg << endl;
		} else {
			cout << "[R] " << msg.msg << endl;
		}
	} else if (msg.tag == "ftp") {
		if (msg.level == logError) {
			cout << "[I] " << msg.msg << endl;
		} else {
			cout << "[I] " << msg.msg << endl;
		}
	}
	return true;
}

ListItemUnix::ListItemUnix() {
	init();
}
ListItemUnix::ListItemUnix(const string& listitem) {
	init();
	parse(listitem);

}
ListItemUnix::~ListItemUnix() {

}
void ListItemUnix::init() {
	dir = false;
	size = 0;
	month = 0;
	day = 0;
	year = 0;
	hour = 0;
	minitue = 0;
	item.clear();
	privilage.clear();
	user.clear();
	group.clear();
	name.clear();
}

int getMonthInt(const string& month) {
	if (month == "Jan") {
		return 1;
	}
	if (month == "Feb") {
		return 2;
	}
	if (month == "Mar") {
		return 3;
	}
	if (month == "Apr") {
		return 4;
	}
	if (month == "May") {
		return 5;
	}
	if (month == "Jun") {
		return 6;
	}
	if (month == "Jul") {
		return 7;
	}
	if (month == "Aug") {
		return 8;
	}
	if (month == "Sep") {
		return 9;
	}
	if (month == "Sept") {
		return 9;
	}
	if (month == "Oct") {
		return 10;
	}
	if (month == "Nov") {
		return 11;
	}
	if (month == "Dec") {
		return 12;
	}

	return 0;
}

//drwxr-xr-x 1 ftp ftp              0 Dec 28 23:46 a
//-rw-r--r-- 1 ftp ftp            161 Oct 30  2012 Main.java
void ListItemUnix::parse(const string& listitem) {
	item = listitem;
	if (item.empty()) {
		return;
	}
	boost::regex nameReg("(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(.+)");
	//						dwrx      1        ftp       ftp      0/161       dec       28     23:46/2012  a b
	boost::smatch mat;
	if (boost::regex_search(item, mat, nameReg)) {
		if (mat.size() == 10) {
			privilage = mat[1];
			user = mat[3];
			group = mat[4];
			size = toint(mat[5]); //tostr
			month = getMonthInt(mat[6]); //int
			day = toint(mat[7]);
			string timeOrYear = mat[8];
			if (isDir()) {
				vector<string> hm = split(timeOrYear, ':');
				hour = toint(hm[0]);
				minitue = toint(hm[1]);
			} else {
				year = toint(timeOrYear);
			}
			name = mat[9];
		}
	}
}
bool ListItemUnix::isDir() {
	if (!item.empty()) {
		return item[0] == 'd';
	}
	return false;
}

string ListItemUnix::getName() {
	return name;
}
int ListItemUnix::getSize() {

	return size;
}
int ListItemUnix::getMonth() {
	return month;
}
int ListItemUnix::getDay() {
	return day;
}
int ListItemUnix::getYear() {
	return year;
}
int ListItemUnix::getHour() {
	return hour;
}
int ListItemUnix::getMinitue() {
	return minitue;
}
string ListItemUnix::getUser() {
	return user;
}
string ListItemUnix::getGroup() {
	return group;
}
string ListItemUnix::getPrivilage() {
	return privilage;
}
DataTransfer::DataTransfer() {
	success = false;
	translating = false;
	dataEvent.reset();
	dataThread.OnMessage = boost::bind(&DataTransfer::msgHandler, this, _1);
	dataThread.start();
}
DataTransfer::~DataTransfer() {
	dataThread.postQuitMessage();
	dataThread.waitFinish();
}
void DataTransfer::beginTrans(void* arg) {
	assert(!translating);
	assert(!OnTransfer.empty());
	translating = true;
	success = false;
	dataEvent.reset();
	Message msg;
	msg.argP = arg;
	dataThread.post(msg);
}
void DataTransfer::msgHandler(Message& msg) {
	if (OnTransfer) {
		success = OnTransfer(msg.argP);
	}
	translating = false;
	dataEvent.notify();
}
void DataTransfer::waitFinish(DWORD timeout) {
	if (translating) {
		dataEvent.wait(timeout);
	}
}
FtpContext::FtpContext() {
	port = 21;
	pasvPort = 0;
	portPort = 0;
	listDataOK = false;
	nlstDataOK = false;
	usePasv = true;

	buf.init(128);
	logConn = slog.pushLogHandler(myFtpLog, "ftp", "ftpsend", "ftpecho");
}

FtpContext::~FtpContext() {
	logConn.disconnect();
}

// data connection thread

bool FtpContext::connect() {
	ftplog << "connecting..." << end;
	if (!sock.createTcpSocket()) {
		return false;
	}
	SocketOption opt(sock.sock);
	opt.setExclusiveAddrUse(TRUE);
	if (0 != sock.connect(host, port)) {
		sock.close();
		return false;
	}
	ftplog << "connected." << end;
	return true;
}
bool FtpContext::recvControl(Packet* p) {
	int n = sock.recv(buf.buffer, buf.length);
	if (n == 0) {
		sock.close();
		ftplog << "Server Closed" << end;
		return false;
	}
	if (n < 0) {
		SocketError e;
		sock.close();
		ftplog << err << p->cmdName() << " " << e.getCode() << sp << e.getCodeName() << end;
		return false;

	}
	//n > 0

	p->appendResponse(string(buf.buffer, n));
	if (p->isWait()) {
		if (OnWait) {
			OnWait(p);
		}
	}
	if (p->isOk() || p->isNext()) {
		return true;
	}
	if (p->isError() || p->isFailed()) {
		return false;
	}
	return recvControl(p);
}

bool FtpContext::sendControl(Packet* p) {
	string s = p->getRequest();
	ftpsend << trimCrlf(s) << end;
	int flag = 0;
	if (p->cmd == FTP_ABOR) {
		flag = MSG_OOB;
	}
	bool b = sock.sendAll(s.c_str(), s.length(), flag);
	if (!b) {
		ftplog << err << "Send Failed" << end;
	}
	return b;
}
bool FtpContext::sendRecvControl(Packet* p) {
	if (!sendControl(p)) {
		return false;
	}
	if (!recvControl(p)) {
		return false;
	}
	return true;
}

bool FtpContext::isSupportCommand(string serverCommand) {
	return serverCommands.find(serverCommand) != serverCommands.end();
}
bool FtpContext::isSupportCommand(int ftpCommand) {
	string s = ftpCmdName(ftpCommand);
	return isSupportCommand(s);
}
vector<string> FtpContext::getNlstItems() {
	vector<string> v;
	if (nlstDataOK) {
		LineParse p(nlstData);
		for (int i = 0; i < p.getLineCount(); ++i) {
			string line = p.getLineText(i);
			line = trim(line);
			if (!line.empty()) {
				v.push_back(line);
			}
		}
	}
	return v;
}
bool FtpContext::isUnix() {
	return systData.find("UNIX") != string::npos;
}
bool FtpContext::isMsDos() {
	return systData.find("DOS") != string::npos;
}

vector<ListItemUnix> FtpContext::getListItemsUnix() {
	vector<ListItemUnix> v;
	if (isUnix() && listDataOK) {
		LineParse p(listData);
		for (int i = 0; i < p.getLineCount(); ++i) {
			string line = p.getLineText(i);
			line = trim(line);
			if (!line.empty()) {
				v.push_back(ListItemUnix(line));
			}
		}
	}
	return v;
}

} /* namespace yet */
